//go:build linux

package http_v3

import (
	"errors"
	"go2/log"
	"net"
	"net/http"
	"runtime"
	"strconv"
	"strings"
	"syscall"
	"time"
)

func NewGoHttp() *GoHttp {
	return &GoHttp{}
}

type GoHttp struct {
}

func (c *GoHttp) Start(server *http.Server, thread_count int) error {
	if server.IdleTimeout <= 0 {
		server.IdleTimeout = 15 * time.Second
	}

	// 使用系统调用,打开一个socket
	svr_fd, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_STREAM|syscall.SOCK_CLOEXEC, syscall.IPPROTO_TCP)
	if err != nil {
		return err
	}
	defer func() {
		if svr_fd > 0 {
			syscall.Close(svr_fd)
		}
	}()

	// ip地址转换
	var addr [4]byte
	temp := strings.Split(server.Addr, ":")
	if len(temp) != 2 {
		return errors.New("addr error")
	}

	// 处理空地址情况（监听所有接口）
	ipStr := temp[0]
	if ipStr == "" {
		ipStr = "0.0.0.0"
	}

	copy(addr[:], net.ParseIP(ipStr).To4())
	port, err := strconv.Atoi(temp[1])
	if err != nil {
		return err
	}

	if err = syscall.Bind(svr_fd, &syscall.SockaddrInet4{Port: port, Addr: addr}); err != nil {
		return err
	}

	// 这个选项可以允许多个Socket绑定到同一个IP地址和端口号上，从而实现端口重用。
	// 这在需要频繁重启或需要处理大量连接的服务器应用中很有用
	if err = syscall.SetsockoptInt(svr_fd, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1); err != nil {
		return err
	}

	// 这个选项可以启用TCP的快速ACK机制，从而减小延迟。
	// 当服务器收到客户端的数据后，可以立即发送ACK包，而不必等待一段时间。
	// 这在需要实时性较高的应用中很有用
	if err = syscall.SetsockoptInt(svr_fd, syscall.IPPROTO_TCP, syscall.TCP_QUICKACK, 1); err != nil {
		return err
	}

	// 这个选项可以启用TCP的心跳检测机制，当连接空闲一段时间后，操作系统会自动发送心跳包来检测连接是否还活着。
	// 这在需要保持长连接的应用中很有用
	if err = syscall.SetsockoptInt(svr_fd, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1); err != nil {
		return err
	}

	// 这个选项可以禁用Nagle算法，从而减小延迟。
	// Nagle算法是一种优化TCP网络传输的算法，它将多个小数据包合并成一个大数据包发送，从而减少网络传输次数，但会增加延迟。
	// 在需要实时性较高的应用中，可以通过设置TCP_NODELAY选项来禁用Nagle算法，从而减小延迟。
	if err = syscall.SetsockoptInt(svr_fd, syscall.IPPROTO_TCP, syscall.TCP_NODELAY, 1); err != nil {
		return err
	}

	size := 2048
	if runtime.NumCPU() > 4 {
		size = runtime.NumCPU() * 512
	}

	// 开启监听
	if err = syscall.Listen(svr_fd, size); err != nil {
		return err
	}

	var pro_map []*GoServer
	var i int

	for i = 0; i < thread_count; i++ {
		pro_map = append(pro_map, NewServer(server))
	}

	pro_id := 0

	for {
		// 获取连接
		i, _, err := syscall.Accept(svr_fd)
		if err != nil {
			log.Errorf("Accept err : %v", err)
			continue
		}
		if pro_id >= thread_count {
			pro_id = 0
		}
		pro_map[pro_id].Push(i)
		pro_id++
	}
}
